package PartE;

import java.util.LinkedList;
import java.util.Queue;

public class ThreadPool {
	
	private final static int DEFAULT_SIZE = 4;
	
	/**
	 * A fixed size array of threads
	 */
	private Thread[] threads;
	
	/**
	 * A FIFO queue of waiting tasks
	 */
	private Queue<Runnable> tasks;
	
	/**
	 * Constructor for a ThreadPool of default size
	 */
	public ThreadPool() {
		this(DEFAULT_SIZE);
	}
	
	/**
	 * Constructor for a ThreadPool
	 * @param size  The number of threads in the pool
	 */
	public ThreadPool(int size) {
		threads = new Thread[size];
		tasks = new LinkedList<Runnable>();
		
		//Populate the array of threads and start each thread
		for (int i = 0 ; i < size ; i++) {
			Thread t = new MyThread(this);
			threads[i]=t;
			t.start();
		}
	}
	
	/**
	 * Add a task to the thread pool
	 * @param task  A Runnable task for the ThreadPool to execute
	 */
	public synchronized void add(Runnable task) {
		tasks.add(task);
		notify();
	}
	
	/**
	 * This is used by the MyThread objects to get the
	 * next task to be executed.  The method is synchronised
	 * so that only the notified thread can take a task from
	 * the list.
	 * @return the next Runnable task
	 */
	public synchronized Runnable getNextJob() {
		
		// if no tasks are in the queue, we wait until notified
		while (tasks.isEmpty()) {
			try {
				wait();
			} catch (InterruptedException e) {
				System.out.println(e.getMessage());
			}
		}
		// if we are here, there must be at least one task on the queue
		return tasks.remove();
	}

	/**
	 * Stop all the threads in the ThreadPool
	 */
	public void stopPool() {
		for (Thread t : threads) {
			t.interrupt();
		}
	}

	
	/**
	 * Inner Class: A simple thread that gets jobs
	 * from the ThreadPool and runs them.
	 * @author phil
	 *
	 */
	private class MyThread extends Thread{
		
		/**
		 * A reference to the owner ThreadPool.
		 * Not really needed for an inner class.
		 */
		ThreadPool pool;
		
		/**
		 * The constructor takes in a reference to the ThreadPool
		 * @param tp  The ThreadPool (owner)
		 */
		public MyThread(ThreadPool tp) {
			super();
			pool = tp;
		}
		
		/**
		 * Keep trying to get jobs and run them
		 */
		public void run() {
			while (true) {
				Runnable job = pool.getNextJob();
				if (job != null) job.run();
			}
		}
	}
}
